package com.my.study.utils;

import com.my.study.exception.BusinessException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 手写简单的分布式锁
 * 保证了原子性、重试、超时释放
 * 暂未实现锁续命
 */
@Component
public class RedisShareUtil {

    private final long TIME_OUT = 10000;

    /**
     * 加锁
     *
     * @param prefix
     * @param key
     * @param value
     * @param timeout
     * @return
     */
    public boolean lock(String prefix, String key, String value, long timeout) {
        boolean result = false;
        if (StringUtils.isBlank(key) || StringUtils.isBlank(value) || timeout == 0) {
            throw BusinessException.fail("加锁失败");
        }
        key = StringUtils.join(prefix, "_", key);
        long currentTimeMillis = System.currentTimeMillis();
        long timeoutTimeMillis = currentTimeMillis + TIME_OUT;
        while (currentTimeMillis < timeoutTimeMillis) {
            result = RedisUtil.setNx(key, value, timeout, TimeUnit.MILLISECONDS);
            if (result) {
                return true;
            }
            currentTimeMillis = System.currentTimeMillis();
        }
        return false;
    }

    /**
     * 释放锁
     *
     * @param key
     * @return
     */
    public boolean unlock(String key){
        if (StringUtils.isBlank(key)) {
            throw BusinessException.fail("锁释放失败");
        }
        return RedisUtil.del(key);
    }

    public void doExecute(String prefix, String key, String value, long timeout,Object object,Prossor prossor){
        boolean lock = lock(prefix, key, value, timeout);
        try{
            if(lock){
                prossor.execute(object);
            }
        }finally {
            unlock(StringUtils.join(prefix,"_",key));
        }
    }

    public interface Prossor{
        String execute(Object object);
    }
}
